package com.lyf.lottery.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.lyf.common.api.CommonResult;
import com.lyf.common.exception.ApiException;
import com.lyf.lottery.constant.Const;
import com.lyf.lottery.dao.mapper.OptActivityBlindBoxMapper;
import com.lyf.lottery.dao.mapper.OptActivityBlindBoxRecordMapper;
import com.lyf.lottery.dao.mapper.OptActivityMapper;
import com.lyf.lottery.model.po.OptActivity;
import com.lyf.lottery.model.po.OptActivityBlindBox;
import com.lyf.lottery.model.po.OptActivityBlindBoxRecord;
import com.lyf.lottery.model.po.Prize;
import com.lyf.lottery.model.req.OptActivityQuery;
import com.lyf.lottery.model.vo.PumpResultVo;
import com.lyf.lottery.service.OptActivityService;
import com.lyf.lottery.util.LotteryUtil;
import com.lyf.lottery.util.RedisKeyUtils;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.RandomUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.io.Serializable;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import static com.lyf.common.api.ResultCode.FAILED;
import static com.lyf.lottery.common.LotteryResultCode.*;
import static com.lyf.lottery.constant.Const.ActivityStatusEnum.*;

/**
 * OptActivity service
 *
 * @author lyf
 **/
@Slf4j
@Service
public class OptActivityServiceImpl implements OptActivityService {

    @Resource
    private OptActivityMapper optActivityMapper;
    @Resource
    private OptActivityBlindBoxMapper blindBoxMapper;
    @Resource
    private OptActivityBlindBoxRecordMapper blindBoxRecordMapper;
    @Resource
    private RedissonClient redissonClient;

    /**
     * 分页查询
     *
     * @param query 查询条件
     * @return PageInfo
     * @date 2020/12/25
     */
    @Override
    public PageInfo<OptActivity> selectPage(OptActivityQuery query) {
        PageHelper.startPage(query.getPageNum(), query.getPageSize());
        List<OptActivity> list = optActivityMapper.selectList(query);
        return new PageInfo<>(list);
    }

    /**
     * 插入数据
     *
     * @param entity entity
     * @return com.lyf.lottery.entity.OptActivity
     * @date 2020/12/25
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public OptActivity insert(OptActivity entity) {

        entity.setStatus(NOT_PUT_ON.getCode());
        entity.setCreateTime(new Date());
        entity.setUpdateTime(new Date());
        optActivityMapper.insert(entity);
        return entity;
    }

    /**
     * 修改数据
     *
     * @param entity entity
     * @date 2020/12/25
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(OptActivity entity) {
        optActivityMapper.updateById(entity);
    }

    /**
     * 获取详情
     *
     * @param id 主键ID
     * @return com.lyf.lottery.entity.OptActivity
     * @date 2020/12/25
     */
    @Override
    public OptActivity selectById(Serializable id) {
        OptActivity optActivity = optActivityMapper.selectById(id);
        return optActivity;
    }

    @Override
    public CommonResult putOn(Integer id) {
        OptActivity optActivity = optActivityMapper.selectById(id);
        if (null == optActivity) {
            return CommonResult.failed(DATA_NOT_EXIST);
        }
        optActivity.setStatus(PUT_ON.getCode());
        optActivity.setUpdateTime(new Date());
        optActivityMapper.updateById(optActivity);
        return CommonResult.success(true);
    }

    @Override
    public CommonResult putDown(Integer id) {
        OptActivity optActivity = optActivityMapper.selectById(id);
        if (null == optActivity) {
            return CommonResult.failed(DATA_NOT_EXIST);
        }
        optActivity.setStatus(PUT_DOWN.getCode());
        optActivity.setUpdateTime(new Date());
        optActivityMapper.updateById(optActivity);
        return CommonResult.success(true);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CommonResult draw(Integer id) {
        CommonResult commonResult = pumpPreCheck(id);
        if (commonResult != null) {
            return commonResult;
        }
        Long userId = RandomUtils.nextLong(1, 100);
        OptActivityBlindBox blindBox = pump(id, userId);
        if (null == blindBox) {
            return CommonResult.failed(FAILED, "服务器繁忙，请稍候再试");
        }
        blindBox = blindBoxMapper.selectById(blindBox.getId());
        PumpResultVo pumpResultVo = pumpAfterDeal(blindBox, userId);
        return CommonResult.success(pumpResultVo);
    }

    private PumpResultVo pumpAfterDeal(OptActivityBlindBox blindBox, Long userId) {
        OptActivityBlindBoxRecord pumpRecord = new OptActivityBlindBoxRecord();
        pumpRecord.setActivityId(blindBox.getActivityId());
        pumpRecord.setPrizeContent(blindBox.getPrizeContent());
        pumpRecord.setPrizeType(blindBox.getPrizeType());
        pumpRecord.setUserId(userId);

        PumpResultVo resultVo = new PumpResultVo();
        resultVo.setPrizeContent(blindBox.getPrizeContent());
        resultVo.setUserId(userId);

        pumpRecord.setWinningFlag(true);
        resultVo.setWinningFlag(true);
        if (blindBox.getPrizeCount() > 0) {
            int updateCount = blindBoxMapper.decrNumAndIncrObtainNum(blindBox.getId(), blindBox.getPrizeCount());
            if (updateCount <= 0) {
                throw new ApiException(FAILED, "服务器繁忙，请稍候再试");
            }

        } else {
            pumpRecord.setWinningFlag(false);
            resultVo.setWinningFlag(false);
        }
        blindBoxRecordMapper.insert(pumpRecord);
        return resultVo;
    }

    private CommonResult pumpPreCheck(Integer activityId) {
        LambdaQueryWrapper<OptActivity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OptActivity::getId, activityId).eq(OptActivity::getStatus, PUT_ON.getCode());
        OptActivity optActivity = optActivityMapper.selectOne(queryWrapper);
        if (optActivity == null) {
            return CommonResult.failed(ACTIVITY_NOT_EXIST);
        }
        long timeMillis = System.currentTimeMillis();
        Long startTime = optActivity.getStartTime().getTime();
        Date endTime = optActivity.getEndTime();
        if (timeMillis < startTime) {
            return CommonResult.failed(CURRENT_TIME_IS_NOT_ACTIVE_TIME, "活动还未开始！");
        }
        if (null != endTime && endTime.getTime() < timeMillis) {
            return CommonResult.failed(CURRENT_TIME_IS_NOT_ACTIVE_TIME, "活动已结束！");
        }
        LambdaQueryWrapper<OptActivityBlindBox> blindBoxQuery = new LambdaQueryWrapper<>();
        blindBoxQuery.eq(OptActivityBlindBox::getActivityId, activityId).eq(OptActivityBlindBox::getStatus, PUT_ON.getCode());
        List<OptActivityBlindBox> optActivityBlindBoxes = blindBoxMapper.selectList(blindBoxQuery);
        if (optActivityBlindBoxes.size() == 0) {
            return CommonResult.failed(BLIND_BOX_NUM_ERROR);
        }
        return null;
    }

    private OptActivityBlindBox pump(Integer activityId, Long userId) {
        LambdaQueryWrapper<OptActivityBlindBox> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OptActivityBlindBox::getActivityId, activityId).eq(OptActivityBlindBox::getStatus, PUT_ON.getCode());
        List<OptActivityBlindBox> optActivityBlindBoxes = blindBoxMapper.selectList(queryWrapper);
        RLock lock = null;
        OptActivityBlindBox blindBox = null;
        String lockName = RedisKeyUtils.getLockName(activityId, userId);
        try {
            lock = redissonClient.getLock(lockName);
            if (lock.tryLock(Const.CONST_10, Const.CONST_10, TimeUnit.SECONDS)) {
                List<Prize> prizes = LotteryUtil.packagePrizeList(optActivityBlindBoxes);
                Integer prizeId = LotteryUtil.lottery(prizes);
                blindBox = blindBoxMapper.selectById(prizeId);
                log.info("抽到了{}", blindBox);
            }
        } catch (InterruptedException e) {
            log.error("抽奖异常,ex:{}", e);
            throw new ApiException("服务器繁忙，请稍候再试");
        } finally {
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return blindBox;
    }
}
